package ar.com.spsolutions.splilabrary.view.components;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;

import ar.com.spsolutions.splibrary.entity.Entity;
import ar.com.spsolutions.splibrary.services.dto.AbstractCRUDDTO;

public class Header<E extends Entity> extends Panel {
	public class ModifyDeletePanel extends Panel {

		private static final long serialVersionUID = 3763646101075719320L;

		public ModifyDeletePanel(final String id, final IModel<E> model,
				final AbstractCRUDDTO<E> genericCRUDDTO,
				final CRUDComponent<E> crudComponent) {
			super(id, model);
			this.add(this.getModifyButton(model.getObject(), genericCRUDDTO,
					crudComponent));
			this.add(this.getDeleteButton(model.getObject(), genericCRUDDTO,
					crudComponent));
		}

		private Component getDeleteButton(final E modelObject,
				final AbstractCRUDDTO<E> genericCRUDDTO,
				final CRUDComponent<E> crudComponent) {
			return new AjaxSubmitLink("deleteButton") {

				private static final long serialVersionUID = 7740728724018667230L;

				@Override
				protected void onSubmit(final AjaxRequestTarget target,
						final Form<?> form) {
					genericCRUDDTO.setModel(modelObject);
					genericCRUDDTO.delete();
					crudComponent.hideForm(target);
				}

				@Override
				protected void onError(final AjaxRequestTarget target,
						final Form<?> form) {
					crudComponent.hideForm(target);
				}
			};
		}

		private AjaxSubmitLink getModifyButton(final E modelObject,
				final AbstractCRUDDTO<E> genericCRUDDTO,
				final CRUDComponent<E> crudComponent) {
			return new AjaxSubmitLink("modifyButton") {

				private static final long serialVersionUID = 7740728724018667230L;

				@Override
				protected void onSubmit(final AjaxRequestTarget target,
						final Form<?> form) {
					genericCRUDDTO.setModel(modelObject);
					crudComponent.showFormFor(genericCRUDDTO, target);
				}

				@Override
				protected void onError(final AjaxRequestTarget target,
						final Form<?> form) {
					genericCRUDDTO.setModel(null);
					crudComponent.hideForm(target);
				}
			};
		}

	}

	private static final long serialVersionUID = -2897433702647251314L;

	private String value;
	private List<E> results = new ArrayList<E>();
	private E selected;

	private Form<AbstractCRUDDTO<E>> mainForm;

	private AjaxFallbackDefaultDataTable<E> resultTable;

	public Header(final String headerId,
			final AbstractCRUDDTO<E> genericCRUDDTO,
			final CRUDComponent<E> crudComponent, final String[] headerColumns) {
		super(headerId);
		this.mainForm = crudComponent.getMainForm();
		this.add(this.makeForm(genericCRUDDTO, crudComponent, headerColumns));
	}

	public Form<AbstractCRUDDTO<E>> getMainForm() {
		return this.mainForm;
	}

	public void setMainForm(final Form<AbstractCRUDDTO<E>> mainForm) {
		this.mainForm = mainForm;
	}

	private Form<Header<E>> makeForm(final AbstractCRUDDTO<E> genericCRUDDTO,
			final CRUDComponent<E> crudComponent, final String... headerColumns) {
		final Form<Header<E>> form = new Form<Header<E>>("searchForm",
				new CompoundPropertyModel<Header<E>>(this));

		final FeedbackPanel feedbackPanel = new FeedbackPanel("feedback");
		feedbackPanel.setOutputMarkupId(true);
		form.add(feedbackPanel);

		form.add(new RequiredTextField<String>("value"));

		final Label searchResultLabel = new Label("searchResultLabel",
				new ResourceModel("searchResultLabel"));
		searchResultLabel.setOutputMarkupId(true);
		searchResultLabel.setOutputMarkupPlaceholderTag(true);
		searchResultLabel.setVisible(false);
		form.add(searchResultLabel);

		this.resultTable = this.getResultTable(headerColumns, genericCRUDDTO,
				crudComponent);
		form.add(this.getResultTable());

		final AjaxSubmitLink searchButton = this.getSearchButton(
				genericCRUDDTO, feedbackPanel, this.getResultTable());
		form.add(searchButton);

		form.add(this.getCreateButton(crudComponent, genericCRUDDTO,
				this.getResultTable(), feedbackPanel));
		return form;
	}

	private AjaxSubmitLink getCreateButton(
			final CRUDComponent<E> crudComponent,
			final AbstractCRUDDTO<E> genericCRUDDTO,
			final AjaxFallbackDefaultDataTable<E> ajaxFallbackDefaultDataTable,
			final FeedbackPanel feedbackPanel) {
		return new AjaxSubmitLink("createButton") {

			private static final long serialVersionUID = -8573256221575930700L;

			@Override
			protected void onSubmit(final AjaxRequestTarget target,
					final Form<?> form) {
				genericCRUDDTO.forCreate(Header.this.getValue());
				crudComponent.showFormFor(genericCRUDDTO, target);
				ajaxFallbackDefaultDataTable.setVisible(false);
				target.add(ajaxFallbackDefaultDataTable);
				target.add(feedbackPanel);
			}

			@Override
			protected void onError(final AjaxRequestTarget target,
					final Form<?> form) {
				crudComponent.hideForm(target);
				target.add(feedbackPanel);
			}
		};
	}

	private AjaxFallbackDefaultDataTable<E> getResultTable(
			final String[] headerColumns,
			final AbstractCRUDDTO<E> genericCRUDDTO,
			final CRUDComponent<E> crudComponent) {
		final List<IColumn<E>> columns = this.getColumns(headerColumns,
				crudComponent, genericCRUDDTO);
		final ISortableDataProvider<E> dataProvider = this
				.getDataProvider(genericCRUDDTO);
		final AjaxFallbackDefaultDataTable<E> table = new AjaxFallbackDefaultDataTable<E>(
				"resultTable", columns, dataProvider, 10);
		table.setVisible(false);
		table.setOutputMarkupPlaceholderTag(true);
		return table;
	}

	private ISortableDataProvider<E> getDataProvider(
			final AbstractCRUDDTO<E> genericCRUDDTO) {
		return new SortableDataProvider<E>() {

			private static final long serialVersionUID = -8390663556413667850L;

			@Override
			public Iterator<? extends E> iterator(final int first,
					final int count) {
				return genericCRUDDTO.getResults(first, count);
			}

			@Override
			public int size() {
				return genericCRUDDTO.allResultsSize();
			}

			@Override
			public IModel<E> model(final E object) {
				return new CompoundPropertyModel<E>(object);
			}
		};
	}

	private List<IColumn<E>> getColumns(final String[] headerColumns,
			final CRUDComponent<E> crudComponent,
			final AbstractCRUDDTO<E> genericCRUDDTO) {
		final List<IColumn<E>> columns = new ArrayList<IColumn<E>>();
		for (final String header : headerColumns) {
			final IModel<String> model = this.getHeaderModel(header);
			final String sortProperty = this.getHeaderSortProperty(header);
			final String propertyExpression = this.getHeader(header);
			this.addColumn(columns, model, sortProperty, propertyExpression);
		}
		return this.addButtonsToHeader(columns, crudComponent, genericCRUDDTO);
	}

	private void addColumn(final List<IColumn<E>> columns,
			final IModel<String> model, final String sortProperty,
			final String propertyExpression) {
		if ((sortProperty == null) || sortProperty.isEmpty()) {
			columns.add(new PropertyColumn<E>(model, propertyExpression));
		} else {
			columns.add(new PropertyColumn<E>(model, sortProperty,
					propertyExpression));
		}
	}

	private List<IColumn<E>> addButtonsToHeader(final List<IColumn<E>> columns,
			final CRUDComponent<E> crudComponent,
			final AbstractCRUDDTO<E> genericCRUDDTO) {
		columns.add(this.getModifyDeletePanel(genericCRUDDTO, crudComponent));
		return columns;
	}

	private IColumn<E> getModifyDeletePanel(
			final AbstractCRUDDTO<E> genericCRUDDTO,
			final CRUDComponent<E> crudComponent) {
		return new AbstractColumn<E>(new ResourceModel("panelHeaderLabel")) {
			private static final long serialVersionUID = -5441484842757918853L;

			@Override
			public void populateItem(final Item<ICellPopulator<E>> cellItem,
					final String componentId, final IModel<E> rowModel) {
				cellItem.add(new ModifyDeletePanel(componentId, rowModel,
						genericCRUDDTO, crudComponent));
			}
		};
	}

	private String getHeader(final String header) {
		return this.getHeaderParts(header)[0];
	}

	private String getHeaderSortProperty(final String header) {
		final String[] headerParts = this.getHeaderParts(header);
		if (headerParts.length < 2) {
			return null;
		} else {
			final String sortableString = headerParts[1];
			return Boolean.parseBoolean(sortableString) ? headerParts[0] : "";
		}
	}

	private String[] getHeaderParts(final String header) {
		return header.split(":");
	}

	private IModel<String> getHeaderModel(final String header) {
		final String[] headerParts = this.getHeaderParts(header);
		return new Model<String>(headerParts[0]);
	}

	private AjaxSubmitLink getSearchButton(
			final AbstractCRUDDTO<E> genericCRUDDTO,
			final FeedbackPanel feedbackPanel,
			final AjaxFallbackDefaultDataTable<E> aResultTable) {
		return new AjaxSubmitLink("searchButton") {

			private static final long serialVersionUID = 3221410045076972044L;

			@Override
			protected void onSubmit(final AjaxRequestTarget target,
					final Form<?> form) {
				aResultTable.modelChanging();
				genericCRUDDTO.setSearchValue(Header.this.getValue());
				genericCRUDDTO.forUpdate();
				aResultTable.modelChanged();
				aResultTable.setVisible(true);
				target.add(aResultTable);
				target.add(aResultTable);
			}

			@Override
			protected void onError(final AjaxRequestTarget target,
					final Form<?> form) {
				target.add(feedbackPanel);
				target.add(aResultTable);
			}
		};
	}

	public String getValue() {
		return this.value;
	}

	public void setValue(final String value) {
		this.value = value;
	}

	public List<E> getResults() {
		return this.results;
	}

	public void setResults(final List<E> results) {
		this.results = results;
	}

	public E getSelected() {
		return this.selected;
	}

	public void setSelected(final E selected) {
		this.selected = selected;
	}

	public void hideResultTable(final AjaxRequestTarget target) {
		this.getResultTable().setVisible(false);
		target.add(this.getResultTable());
	}

	public AjaxFallbackDefaultDataTable<E> getResultTable() {
		return this.resultTable;
	}

	public void setResultTable(final AjaxFallbackDefaultDataTable<E> resultTable) {
		this.resultTable = resultTable;
	}

}
